// 版权所有（c）2019 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package edwards25519

import "sync"

// basepointTable是一组32个affineLookupTables，其中生成表i
// 从256i*基点开始。它是第一次使用时预先计算的。
func basepointTable() *[32]affineLookupTable {
	basepointTablePrecomp.initOnce.Do(func() {
		p := NewGeneratorPoint()
		for i := 0; i < 32; i++ {
			basepointTablePrecomp.table[i].FromP3(p)
			for j := 0; j < 8; j++ {
				p.Add(p, p)
			}
		}
	})
	return &basepointTablePrecomp.table
}

var basepointTablePrecomp struct {
	table    [32]affineLookupTable
	initOnce sync.Once
}

// ScalarBaseMult集合v=x*B，其中B是规范生成器，并且
// 返回v。
// None
// 标量乘法是在恒定时间内完成的。
func (v *Point) ScalarBaseMult(x *Scalar) *Point {
	basepointTable := basepointTable()

	// 写x=sum（x_i*16^i）所以x*B=sum（B*x_i*16^i）
	// 如Ed25519文件所述
	// None
	// 群奇偶系数
	// x*B=x_0*16^0*B+x_2*16^2*B+…+x_62*16^62*B
	// +x_1*16^1*B+x_3*16^3*B+…+x_63*16^63*B
	// x*B=x_0*16^0*B+x_2*16^2*B+…+x_62*16^62*B
	// +16*（x_1*16^0*B+x_3*16^2*B+…+x_63*16^62*B）
	// None
	// 我们对每个i使用一个查找表来获得x_i*16^（2*i）*B
	// 然后做四次乘法乘以16。
	digits := x.signedRadix16()

	multiple := &affineCached{}
	tmp1 := &projP1xP1{}
	tmp2 := &projP2{}

	// 先累加奇数分量
	v.Set(NewIdentityPoint())
	for i := 1; i < 64; i += 2 {
		basepointTable[i/2].SelectInto(multiple, digits[i])
		tmp1.AddAffine(v, multiple)
		v.fromP1xP1(tmp1)
	}

	// 乘以16
	tmp2.FromP3(v)       // tmp2=P2坐标中的v
	tmp1.Double(tmp2)    // P1xP1坐标中的tmp1=2*v
	tmp2.FromP1xP1(tmp1) // tmp2=P2坐标中的2*v
	tmp1.Double(tmp2)    // P1xP1坐标中的tmp1=4*v
	tmp2.FromP1xP1(tmp1) // tmp2=P2坐标中的4*v
	tmp1.Double(tmp2)    // P1xP1坐标中的tmp1=8*v
	tmp2.FromP1xP1(tmp1) // 在P2坐标中，tmp2=8*v
	tmp1.Double(tmp2)    // P1xP1坐标中的tmp1=16*v
	v.fromP1xP1(tmp1)    // 现在v=16*（奇数分量）

	// 累加均匀分量
	for i := 0; i < 64; i += 2 {
		basepointTable[i/2].SelectInto(multiple, digits[i])
		tmp1.AddAffine(v, multiple)
		v.fromP1xP1(tmp1)
	}

	return v
}

// ScalarMult设置v=x*q，并返回v。
// None
// 标量乘法是在恒定时间内完成的。
func (v *Point) ScalarMult(x *Scalar, q *Point) *Point {
	checkInitialized(q)

	var table projLookupTable
	table.FromP3(q)

	// 写入x=总和（x_i*16^i）
	// 所以x*Q=sum（Q*x_i*16^i）
	// =Q*x_0+16*（Q*x_1+16*（…+Q*x_63）…）
	// <----由内而外计算---------
	// None
	// 我们使用查找表来获得x_i*Q值
	// 然后做四次乘法来计算16*Q
	digits := x.signedRadix16()

	// 展开第一次循环迭代以保存计算16*标识
	multiple := &projCached{}
	tmp1 := &projP1xP1{}
	tmp2 := &projP2{}
	table.SelectInto(multiple, digits[63])

	v.Set(NewIdentityPoint())
	tmp1.Add(v, multiple) // 在P1xP1坐标中，tmp1=x_63*Q
	for i := 62; i >= 0; i-- {
		tmp2.FromP1xP1(tmp1) // tmp2=（上一个）在P2坐标中
		tmp1.Double(tmp2)    // P1xP1坐标中的tmp1=2*（上一个）
		tmp2.FromP1xP1(tmp1) // P2坐标中的tmp2=2*（上一个）
		tmp1.Double(tmp2)    // P1xP1坐标中的tmp1=4*（上一个）
		tmp2.FromP1xP1(tmp1) // P2坐标中的tmp2=4*（上一个）
		tmp1.Double(tmp2)    // P1xP1坐标中的tmp1=8*（上一个）
		tmp2.FromP1xP1(tmp1) // P2坐标中的tmp2=8*（上一个）
		tmp1.Double(tmp2)    // P1xP1坐标中的tmp1=16*（上一个）
		v.fromP1xP1(tmp1)    // P3坐标中的v=16*（上一个）
		table.SelectInto(multiple, digits[i])
		tmp1.Add(v, multiple) // 在P1xP1坐标中，tmp1=x_i*Q+16*（上一个）
	}
	v.fromP1xP1(tmp1)
	return v
}

// basepointNafTable是基点的nafLookupTable8。
// 它是第一次使用时预先计算的。
func basepointNafTable() *nafLookupTable8 {
	basepointNafTablePrecomp.initOnce.Do(func() {
		basepointNafTablePrecomp.table.FromP3(NewGeneratorPoint())
	})
	return &basepointNafTablePrecomp.table
}

var basepointNafTablePrecomp struct {
	table    nafLookupTable8
	initOnce sync.Once
}

// VarTimeDoubleScalarBaseMult集合v=a*a+b*b，其中b是规范的
// 生成器，并返回v。
// None
// 执行时间取决于输入。
func (v *Point) VarTimeDoubleScalarBaseMult(a *Scalar, A *Point, b *Scalar) *Point {
	checkInitialized(A)

	// 与单变量基方法类似，我们计算
	// 并将其与查找表一起使用。但是因为
	// 我们可以做可变时间的操作，但我们不能
	// 需要固定时间查找或固定时间数字
	// 计算。
	// None
	// 所以我们用一个宽度为w的非相邻形式来代替
	// 基数16。这类似于二进制表示（一位数）
	// 对于每个二进制位置），但我们允许数字在
	// 幅度高达2^{w-1}，因此非零数字如下
	// 尽可能稀疏。直觉上，这“浓缩”了
	// 稀疏系数上的标量“质量”（意思是
	// 添加的内容更少）。

	basepointNafTable := basepointNafTable()
	var aTable nafLookupTable5
	aTable.FromP3(A)
	// 因为基点是固定的，所以我们可以使用更广泛的NAF
	// 对应于一个更大的表。
	aNaf := a.nonAdjacentForm(5)
	bNaf := b.nonAdjacentForm(8)

	// 找到第一个非零系数。
	i := 255
	for j := i; j >= 0; j-- {
		if aNaf[j] != 0 || bNaf[j] != 0 {
			break
		}
	}

	multA := &projCached{}
	multB := &affineCached{}
	tmp1 := &projP1xP1{}
	tmp2 := &projP2{}
	tmp2.Zero()

	// 从高位移动到低位，使累加器加倍
	// 在每次迭代中，检查是否存在非零
	// 要查找的系数的倍数。
	for ; i >= 0; i-- {
		tmp1.Double(tmp2)

		// 只有当我们要添加非零系数时才更新v。
		if aNaf[i] > 0 {
			v.fromP1xP1(tmp1)
			aTable.SelectInto(multA, aNaf[i])
			tmp1.Add(v, multA)
		} else if aNaf[i] < 0 {
			v.fromP1xP1(tmp1)
			aTable.SelectInto(multA, -aNaf[i])
			tmp1.Sub(v, multA)
		}

		if bNaf[i] > 0 {
			v.fromP1xP1(tmp1)
			basepointNafTable.SelectInto(multB, bNaf[i])
			tmp1.AddAffine(v, multB)
		} else if bNaf[i] < 0 {
			v.fromP1xP1(tmp1)
			basepointNafTable.SelectInto(multB, -bNaf[i])
			tmp1.SubAffine(v, multB)
		}

		tmp2.FromP1xP1(tmp1)
	}

	v.fromP2(tmp2)
	return v
}
